﻿<%@ WebHandler Language="C#" Class="HttpCombiner" %>

using System;
using System.Net;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Configuration;
using System.Web;
using System.Web.Caching;
public class HttpCombiner : IHttpHandler {

    private const bool DO_GZIP = true;
    private readonly static TimeSpan CACHE_DURATION = TimeSpan.FromDays(30);
    
    public void ProcessRequest (HttpContext context) {
        
        HttpRequest request = context.Request;
        
        string setName = request["s"] ?? string.Empty;
        string contentType = request["t"] ?? string.Empty;
        if (string.IsNullOrEmpty(contentType))
        {
            contentType = "type/javascript";
        }
        else
        {
            if (contentType=="js")
            {
                contentType = "type/javascript";
            }
            else if (contentType == "css")
            {
                contentType = "text/css";
            }
            else
            {
                contentType = "text/plain";
            }
        }
        bool isCompressed = DO_GZIP && CanGZip(context.Request);

        UTF8Encoding encoding = new UTF8Encoding(false);

        if (!WriteFromCache(context, setName, isCompressed, contentType))
        {
            System.Collections.Generic.List<string> dependencyFiles = new System.Collections.Generic.List<string>();
            using (MemoryStream memoryStream = new MemoryStream(5000))
            {
                using (Stream writer = isCompressed ?
                    (Stream)(new GZipStream(memoryStream, CompressionMode.Compress)) :
                    memoryStream)
                {
                    string[] fileNames = setName.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string fileName in fileNames)
                    {
                        byte[] fileBytes = GetFileBytes(context, fileName.Trim(), encoding, dependencyFiles);
                        writer.Write(fileBytes, 0, fileBytes.Length);
                    }
                    writer.Close();
                }
                byte[] responseBytes = memoryStream.ToArray();
                context.Cache.Insert(GetCacheKey(setName, isCompressed), responseBytes, new CacheDependency(dependencyFiles.ToArray()), System.Web.Caching.Cache.NoAbsoluteExpiration, CACHE_DURATION);
                WriteBytes(responseBytes, context, isCompressed, contentType);
            }
        }
    }

    private static byte[] GetFileBytes(HttpContext context, string virtualPath, Encoding encoding, System.Collections.Generic.List<string> depencesFile)
    {
        if (virtualPath.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase))
        {
            using (WebClient client = new WebClient())
            {
                return client.DownloadData(virtualPath);
            }
        }
        else
        {
            string physicalPath = context.Server.MapPath(virtualPath);
            depencesFile.Add(physicalPath);
            byte[] bytes = File.ReadAllBytes(physicalPath);
            return bytes;
        }
    }

    private static bool WriteFromCache(HttpContext context, string setName,
        bool isCompressed, string contentType)
    {
        byte[] responseBytes = context.Cache[GetCacheKey(setName, isCompressed)] as byte[];
        if (null == responseBytes || 0 == responseBytes.Length) return false;
        WriteBytes(responseBytes, context, isCompressed, contentType);
        return true;
    }

    private static void WriteBytes(byte[] bytes, HttpContext context, 
        bool isCompressed, string contentType)
    {
        HttpResponse response = context.Response;
        response.AppendHeader("Content-Length", bytes.Length.ToString());
        response.ContentType = contentType;
        if (isCompressed) response.AppendHeader("Content-Encoding", "gzip");
        context.Response.Cache.SetCacheability(HttpCacheability.Public);
        context.Response.Cache.SetExpires(DateTime.Now.Add(CACHE_DURATION));
        context.Response.Cache.SetMaxAge(CACHE_DURATION);
        context.Response.Cache.AppendCacheExtension("must-revalidate, proxy-revalidate");
        response.OutputStream.Write(bytes, 0, bytes.Length);
        response.Flush();
    }

    private static bool CanGZip(HttpRequest request)
    {
        string acceptEncoding = request.Headers["Accept-Encoding"];
        if (!string.IsNullOrEmpty(acceptEncoding) && (acceptEncoding.Contains("gzip") || acceptEncoding.Contains("deflate")))
            return true;
        return false;
    }

    private static string GetCacheKey(string setName, bool isCompressed)
    {
        return "HttpCombiner." + setName  + "." + isCompressed;
    }

    public bool IsReusable
    {
        get
        {
            return true;
        }    }    }    